﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using SevenZip;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace JTS_Archiver
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            if (keyData == (Keys.Control | Keys.A))
            {
                foreach (ListViewItem item in listView1.Items)
                {
                    item.Selected = true;
                }
                return true;
            }
            return base.ProcessCmdKey(ref msg, keyData);
        }

        public string archName = "";
        public string extractDir = "";
        public SevenZipExtractor zipengine;
        public int newperc = 0;
        public string selCurFile = "";
        public bool onlyViewing = false;

        public bool IsElevated
        {
            get
            {
                return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
            }
        }

        public string adminTitle()
        {
            if (IsElevated)
            {
                return " [Admin]";
            }
            else
            {
                return "";
            }
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            // Open archive
            OpenFileDialog fo = new OpenFileDialog();
            fo.Filter = "Supported Archives (*.shitloads)|*.7z;*.zip;*.rar;*.cab;*.ace;*.tar;*.gz;*.bz2;*.xz;*.wim;*.apm;*.arj;*.chm;*.cpio;*.deb;*.flv;*.jar;*.lha;*.lzh;*.lzma;*.rpm;*.swf;*.xar;*.z;*.iso;*.udf;*.vhd|All Files (*.*)|*.*";
            fo.ShowDialog();
            if (fo.FileName != "")
            {
                statusStrip1.Items[1].Text = "Loading: " + Path.GetFileName(fo.FileName);
                Form1.ActiveForm.Text = "JTS Archiver [" + Path.GetFileName(fo.FileName) + "]"+adminTitle();
                archName = fo.FileName;
                zipengine = loadArchive(fo.FileName);
            }
        }

        public string fSize(ulong sz)
        {
            if (sz < 1024) { return Convert.ToString(sz) + "B"; }
            else if ((sz >= 1024) && (sz < 1048576)) { return Convert.ToString(Math.Round((decimal)sz / 1024, 2)) + "KB"; }
            else if ((sz >= 1048576) && (sz < 1073741824)) { return Convert.ToString(Math.Round((decimal)sz / 1048576, 2)) + "MB"; }
            else if (sz >= 1073741824) { return Convert.ToString(Math.Round((decimal)sz / 1073741824, 2))+"GB"; }
            else { return "N/A"; }
        }

        public static System.Drawing.Icon GetFileIcon(string name, bool linkOverlay)
        {
            Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO();
            uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES;
            if (true == linkOverlay) flags += Shell32.SHGFI_LINKOVERLAY;
            flags += Shell32.SHGFI_SMALLICON;
            Shell32.SHGetFileInfo(name, Shell32.FILE_ATTRIBUTE_NORMAL, ref shfi, (uint)System.Runtime.InteropServices.Marshal.SizeOf(shfi), flags);
            System.Drawing.Icon icon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shfi.hIcon).Clone();
            User32.DestroyIcon(shfi.hIcon);
            return icon;
        }

        public SevenZipExtractor loadArchive(string fName)
        {
            listView1.Items.Clear();
            imageList1.Images.Clear();
            listView1.SmallImageList = imageList1;
            SevenZipExtractor engine = new SevenZipExtractor(fName);
            engine.Extracting += new EventHandler<ProgressEventArgs>(progExtracting);
            engine.FileExtractionStarted += new EventHandler<FileInfoEventArgs>(progExtractStarted);
            engine.FileExists += new EventHandler<FileOverwriteEventArgs>(progFileExists);
            engine.ExtractionFinished += new EventHandler<EventArgs>(progExtractDone);
            int i;
            toolStripProgressBar1.Value = 0;
            toolStripProgressBar1.Maximum = engine.ArchiveFileData.Count;
            listView1.BeginUpdate();
            for (i = 0; i < engine.ArchiveFileData.Count; i++)
            {
                Form1.ActiveForm.Refresh();
                toolStripProgressBar1.Value = i;
                if (!engine.ArchiveFileData[i].IsDirectory)
                {
                    Icon appIcon = GetFileIcon(engine.ArchiveFileData[i].FileName, false);
                    imageList1.Images.Add(appIcon);
                    ListViewItem itm = new ListViewItem("");
                    itm.ImageIndex = imageList1.Images.Count - 1;
                    itm.SubItems.Add(Path.GetFileName(engine.ArchiveFileData[i].FileName));
                    itm.SubItems.Add(fSize(engine.ArchiveFileData[i].Size));
                    itm.SubItems.Add(Path.GetDirectoryName(engine.ArchiveFileData[i].FileName));
                    listView1.Items.Add(itm);
                }
            }
            listView1.EndUpdate();
            toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
            statusStrip1.Items[1].Text = "Loaded: " + Path.GetFileName(archName);
            toolStripProgressBar1.Value = 0;
            return engine;
        }

        private void toolStripButton2_Click(object sender, EventArgs e)
        {
            toolStripButton2.ShowDropDown();
        }

        public void extractFiles(string dir)
        {
            extractDir = dir;
            toolStripProgressBar1.Maximum = (int)zipengine.FilesCount;
            toolStripProgressBar1.Value = 0;
            zipengine.BeginExtractArchive(dir);
        }

        private void progExtracting(object sender, ProgressEventArgs e)
        {
            toolStripStatusLabel2.Text = Convert.ToString(e.PercentDone) + "%";
            //statusStrip1.Refresh();
        }
        private void progExtractStarted(object sender, FileInfoEventArgs e)
        {
            toolStripProgressBar1.Increment(1);
            toolStripProgressBar1.ProgressBar.Refresh();
            toolStripStatusLabel1.Text = "Extracting: " + Path.GetFileName(e.FileInfo.FileName);
            statusStrip1.Refresh();
        }
        private void progFileExists(object sender, FileOverwriteEventArgs e)
        {
            //
        }
        private void progExtractDone(object sender, EventArgs e)
        {
            toolStripProgressBar1.Value = 0;
            statusStrip1.Items[1].Text = "Extraction complete!";
            statusStrip1.Items[2].Text = "";
            if (onlyViewing == false)
            {
                if (MessageBox.Show("Extraction completed successfully!" + Environment.NewLine + Environment.NewLine + "Open target directory?", "All Good", MessageBoxButtons.YesNo, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1) == System.Windows.Forms.DialogResult.Yes)
                {
                    Process.Start(@"" + extractDir);
                }
            }
            else { onlyViewing = false; }
        }

        private void Form1_Shown(object sender, EventArgs e)
        {
            Form1.ActiveForm.Text = Form1.ActiveForm.Text + adminTitle();
            string[] args = Environment.GetCommandLineArgs();
            if (args.Count() > 1)
            {
                if (File.Exists(args[1])) {
                    statusStrip1.Items[1].Text = "Loading: " + Path.GetFileName(args[1]);
                    Form1.ActiveForm.Text = "JTS Archiver [" + Path.GetFileName(args[1]) + "]"+adminTitle();
                    archName = args[1];
                    zipengine = loadArchive(args[1]);
                }
            }
        }

        private void extractAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (archName != "")
            {
                FolderBrowserDialog fd = new FolderBrowserDialog();
                fd.Description = "Choose path to extract files..";
                if (fd.ShowDialog() == DialogResult.OK)
                {
                    extractFiles(fd.SelectedPath);
                }
            }
            else
            {
                MessageBox.Show("You need to open an archive first!", "Oops!", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void extractSelectedToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (archName != "")
            {
                if (listView1.SelectedItems.Count > 0)
                {
                    FolderBrowserDialog fd = new FolderBrowserDialog();
                    fd.Description = "Choose path to extract files..";
                    if (fd.ShowDialog() == DialogResult.OK)
                    {
                        extractSelected(fd.SelectedPath);
                    }
                }
                else
                    {
                        MessageBox.Show("Nothing is selected!", "Oops!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            else
            {
                MessageBox.Show("You need to open an archive first!", "Oops!", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        public void extractSelected(string dir)
        {
            extractDir = dir;
            toolStripProgressBar1.Maximum = listView1.SelectedItems.Count;
            toolStripProgressBar1.Value = 0;

            int i;
            List<string> fileList = new List<string>();
            for (i = 0; i < listView1.SelectedItems.Count; i++)
            {
                string itm;
                if (listView1.SelectedItems[i].SubItems[3].Text == "")
                {
                    itm = listView1.SelectedItems[i].SubItems[1].Text;
                }
                else
                {
                    itm = listView1.SelectedItems[i].SubItems[3].Text + "\\" + listView1.SelectedItems[i].SubItems[1].Text;
                }
                fileList.Add(itm);
            }
            zipengine.ExtractFiles(dir, fileList.ToArray());           
        }

        public void extractAndRun(ListViewItem itm, string editor)
        {
            string tmpdir = Path.GetTempPath();
            string fn;
            if (itm.SubItems[3].Text == "")
            {
                fn = itm.SubItems[1].Text;
            }
            else
            {
                fn = itm.SubItems[3].Text + "\\" + itm.SubItems[1].Text;
            }
            string outpath = tmpdir + "\\" + itm.SubItems[1].Text;
            
            StreamWriter fs = new StreamWriter(outpath, false);
            onlyViewing = true;
            zipengine.ExtractFile(fn, fs.BaseStream);
            fs.Close();
            if (editor == "")
            {
                Process.Start(@""+outpath);
            }
            else
            {
                Process.Start(@""+editor, outpath);
            }
        }

        private void inAnotherEditorToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (listView1.SelectedItems.Count > 0)
            {
                OpenFileDialog ed = new OpenFileDialog();
                ed.Filter = "Application (*.exe)|*.exe|All Files (*.*)|*.*";
                if (ed.ShowDialog() == DialogResult.OK)
                {
                    extractAndRun(listView1.SelectedItems[0], ed.FileName);
                }
            }
            else
            {
                MessageBox.Show("Nothing selected!", "Oops!", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void inNotepadToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (listView1.SelectedItems.Count > 0)
            {

                extractAndRun(listView1.SelectedItems[0], "c:\\windows\\system32\\notepad.exe");
            }
            else
            {
                MessageBox.Show("Nothing selected!", "Oops!", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void listView1_DoubleClick(object sender, EventArgs e)
        {
            if (listView1.SelectedItems.Count > 0)
            {
                extractAndRun(listView1.SelectedItems[0], "");
            }
            else
            {
                MessageBox.Show("Nothing selected!", "Oops!", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void runThisFileToolStripMenuItem_Click(object sender, EventArgs e)
        {
            listView1_DoubleClick(sender, e);
        }

        private void toolStripButton3_Click(object sender, EventArgs e)
        {
            using (about aboutbox = new about())
            {
                aboutbox.ShowDialog(this);
            }
        }

        private void toolStripButton4_Click(object sender, EventArgs e)
        {
            using (settings settingsbox = new settings(this))
            {
                settingsbox.ShowDialog(this);
            }
        }

        public void settingsQuit()
        {
            Close();
        }

        private void toolStripButton5_Click(object sender, EventArgs e)
        {
            using (compress compressFrm = new compress(this))
            {
                compressFrm.ShowDialog(this);
            }
        }
        public void beginCompression(string in_archFile, OutArchiveFormat in_archType, CompressionLevel in_compLevel, string[] in_fileList)
        {
            using (progresscompress pcompressFrm = new progresscompress(this, in_archFile, in_archType, in_compLevel, in_fileList))
            {
                pcompressFrm.ShowDialog();
            }
        }
    }

    /// <summary>
    /// Wraps necessary Shell32.dll structures and functions required to retrieve Icon Handles using SHGetFileInfo. Code
    /// courtesy of MSDN Cold Rooster Consulting case study.
    /// </summary>
    /// 

    public class Shell32
    {

        public const int MAX_PATH = 256;
        [StructLayout(LayoutKind.Sequential)]
        public struct SHITEMID
        {
            public ushort cb;
            [MarshalAs(UnmanagedType.LPArray)]
            public byte[] abID;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct ITEMIDLIST
        {
            public SHITEMID mkid;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BROWSEINFO
        {
            public IntPtr hwndOwner;
            public IntPtr pidlRoot;
            public IntPtr pszDisplayName;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpszTitle;
            public uint ulFlags;
            public IntPtr lpfn;
            public int lParam;
            public IntPtr iImage;
        }

        // Browsing for directory.
        public const uint BIF_RETURNONLYFSDIRS = 0x0001;
        public const uint BIF_DONTGOBELOWDOMAIN = 0x0002;
        public const uint BIF_STATUSTEXT = 0x0004;
        public const uint BIF_RETURNFSANCESTORS = 0x0008;
        public const uint BIF_EDITBOX = 0x0010;
        public const uint BIF_VALIDATE = 0x0020;
        public const uint BIF_NEWDIALOGSTYLE = 0x0040;
        public const uint BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX);
        public const uint BIF_BROWSEINCLUDEURLS = 0x0080;
        public const uint BIF_BROWSEFORCOMPUTER = 0x1000;
        public const uint BIF_BROWSEFORPRINTER = 0x2000;
        public const uint BIF_BROWSEINCLUDEFILES = 0x4000;
        public const uint BIF_SHAREABLE = 0x8000;

        [StructLayout(LayoutKind.Sequential)]
        public struct SHFILEINFO
        {
            public const int NAMESIZE = 80;
            public IntPtr hIcon;
            public int iIcon;
            public uint dwAttributes;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
            public string szDisplayName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAMESIZE)]
            public string szTypeName;
        };

        public const uint SHGFI_ICON = 0x000000100;     // get icon
        public const uint SHGFI_DISPLAYNAME = 0x000000200;     // get display name
        public const uint SHGFI_TYPENAME = 0x000000400;     // get type name
        public const uint SHGFI_ATTRIBUTES = 0x000000800;     // get attributes
        public const uint SHGFI_ICONLOCATION = 0x000001000;     // get icon location
        public const uint SHGFI_EXETYPE = 0x000002000;     // return exe type
        public const uint SHGFI_SYSICONINDEX = 0x000004000;     // get system icon index
        public const uint SHGFI_LINKOVERLAY = 0x000008000;     // put a link overlay on icon
        public const uint SHGFI_SELECTED = 0x000010000;     // show icon in selected state
        public const uint SHGFI_ATTR_SPECIFIED = 0x000020000;     // get only specified attributes
        public const uint SHGFI_LARGEICON = 0x000000000;     // get large icon
        public const uint SHGFI_SMALLICON = 0x000000001;     // get small icon
        public const uint SHGFI_OPENICON = 0x000000002;     // get open icon
        public const uint SHGFI_SHELLICONSIZE = 0x000000004;     // get shell size icon
        public const uint SHGFI_PIDL = 0x000000008;     // pszPath is a pidl
        public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;     // use passed dwFileAttribute
        public const uint SHGFI_ADDOVERLAYS = 0x000000020;     // apply the appropriate overlays
        public const uint SHGFI_OVERLAYINDEX = 0x000000040;     // Get the index of the overlay

        public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
        public const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;

        [DllImport("Shell32.dll")]
        public static extern IntPtr SHGetFileInfo(
            string pszPath,
            uint dwFileAttributes,
            ref SHFILEINFO psfi,
            uint cbFileInfo,
            uint uFlags
            );
    }

    /// <summary>
    /// Wraps necessary functions imported from User32.dll. Code courtesy of MSDN Cold Rooster Consulting example.
    /// </summary>
    public class User32
    {
        /// <summary>
        /// Provides access to function required to delete handle. This method is used internally
        /// and is not required to be called separately.
        /// </summary>
        /// <param name="hIcon">Pointer to icon handle.</param>
        /// <returns>N/A</returns>
        [DllImport("User32.dll")]
        public static extern int DestroyIcon(IntPtr hIcon);
    }
}
